home *** CD-ROM | disk | FTP | other *** search
/ SGI Developer Toolbox 6.1 / SGI Developer Toolbox 6.1 - Disc 4.iso / src / exampleCode / opengl / xlib / tabletogl.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-08-02  |  23.5 KB  |  638 lines

  1. /*
  2.  * Copyright 1993, 1994, Silicon Graphics, Inc.
  3.  * All Rights Reserved.
  4.  *
  5.  * This is UNPUBLISHED PROPRIETARY SOURCE CODE of Silicon Graphics, Inc.;
  6.  * the contents of this file may not be disclosed to third parties, copied or
  7.  * duplicated in any form, in whole or in part, without the prior written
  8.  * permission of Silicon Graphics, Inc.
  9.  *
  10.  * RESTRICTED RIGHTS LEGEND:
  11.  * Use, duplication or disclosure by the Government is subject to restrictions
  12.  * as set forth in subdivision (c)(1)(ii) of the Rights in Technical Data
  13.  * and Computer Software clause at DFARS 252.227-7013, and/or in similar or
  14.  * successor clauses in the FAR, DOD or NASA FAR Supplement. Unpublished -
  15.  * rights reserved under the Copyright Laws of the United States.
  16.  */
  17. /*
  18.  * tabletogl.c :  an openGL-Xlib tablet "line-drawing" demo program.  
  19.  *                This is the "after" version, ported from its 4.0 
  20.  *                GLX mixed model "before" counterpart, located at 
  21.  *                ../../GLX/tablet/tabletglx.c
  22.  *
  23.  *                tabletogl imitates a pen (stylus) drawing on a surface:
  24.  *                while the pen (stylus' button) is pressed down, a line
  25.  *                continues to be drawn.  when the pen is released, the 
  26.  *                current line stops.  
  27.  *
  28.  *   There are a function prior to the infinite loop worth noting:
  29.  *
  30.  *       setupdevs() finds, opens, and creates a handle to the "tablet" 
  31.  *       device structure.  it then determines the given type and class 
  32.  *       of each device event we're going to be interested in, and then 
  33.  *       makes requests to the server to send us events that match the 
  34.  *       events and devices described by the event list *and* that come 
  35.  *       from our specific window.
  36.  *
  37.  *    Following this, the get/process input infinite loop occupies the
  38.  *    rest of the program's energies.  the core of this is the "default"
  39.  *    portion of the "switch (event.type)" statement which catches the
  40.  *    tablet events being generated.  notice this is where the
  41.  *    {tablet_motion_type, tablet_press_type, tablet_release_type} vars 
  42.  *    come into play:  recall these were defined in setupdevs() with the 
  43.  *    3 macros DeviceMotionNotify, DeviceButtonPress, and 
  44.  *    DeviceButtonRelease, respectively.  XSelectExtensionEvent then was 
  45.  *    used to ask the server to send us any events generated by these 
  46.  *    devices in our window.
  47.  
  48.  *                                     ratmandu -- ported to openGL, aug 93
  49.  */
  50. #include <stdio.h>
  51. #include <stdlib.h>
  52. #include <GL/gl.h>
  53. #include <GL/glx.h>
  54. #include <GL/glu.h>
  55. #include <X11/Xlib.h>
  56. #include <X11/Xutil.h>
  57. #include <X11/Xos.h>
  58. #include <X11/Xatom.h>
  59. #include <X11/extensions/XI.h>
  60. #include <X11/extensions/XInput.h>
  61.  
  62. #define YELLOW 3
  63. #define BLUE   4
  64. #define CYAN   6
  65.  
  66. #define X 0
  67. #define Y 1
  68. #define MAXRESOLUTION 2206
  69.  
  70.    /* line drawing structures to create a new linked-list everytime the
  71.     * stylus (button) is pressed down and grow that list as long as
  72.     * MotionNotify events occur.  when the stylus (button) is released,
  73.     * free up the list and reset the head and tail to point to null
  74.     */
  75. #define newll()  ((struct lineElement *)malloc(sizeof(struct lineElement)))
  76.  
  77. struct lineElement {
  78.     long xy[2];
  79.     struct lineElement *next;
  80. };
  81.  
  82. typedef struct {
  83.     struct lineElement *head, *tail;
  84. } LineList;
  85.  
  86. LineList lineLs;
  87. struct lineElement *lineDummy;
  88.  
  89. Display *dpy;                                    /* The X server connection */
  90. Atom del_atom;                                   /* WM_DELETE_WINDOW atom   */
  91. Window glwin;                                    /* handle to the GL window */
  92.  
  93. static void openwindow(char *);
  94. static void setupdevs(void);
  95. static void makeframe(void);
  96. static void drawcurrentline(void);
  97. static void clean_exit(void);
  98. static void makeRasterFont(Display **dpy);
  99. static void printString(char *s);
  100.  
  101. int tablet_device_id;                            /* device handles for the  */
  102. int tablet_press_type;      /* the following 3 tablet device ID handles are */
  103. int tablet_release_type;    /* defined in setupdevs() and then used in the  */
  104. int tablet_motion_type;     /* infinite (get/process input) loop in main    */
  105.  
  106. float ratio;         /* stores the current ratio to scale the tablet's full */
  107.                      /* range of coordinates into the window's current size */
  108. int xsize, ysize;                    /* stores window's current size values */
  109. XEvent event;
  110.  
  111.  
  112.  
  113. void main(int argc, char *argv[])
  114. {
  115.     int i;
  116.     long xprev, yprev; /* used if either the x or y stylus val doesn't chng */
  117.     int myExpose, myConfigure, myMotion,
  118.         myButtPress, myButtRelease, myButtDown; /* store which events occur */
  119.  
  120.  
  121.  
  122.     myExpose = myConfigure = myMotion = GL_FALSE;
  123.     myButtPress = myButtRelease = myButtDown = GL_FALSE;
  124.  
  125.     openwindow(argv[0]);     /* open "window" something like winopen wud do */
  126.     setupdevs();            /* make the necessary connections to the tablet */
  127.     makeRasterFont(&dpy);                                      /* make font */
  128.  
  129.     /*
  130.      * The event loop. 
  131.      */
  132.     while (1) {          /* standard logic:  get event(s), process event(s) */
  133.  
  134.         int axis_data[6];
  135.  
  136.         glFlush();                            /* For proper DGL performance */
  137.  
  138.     /* this "do while" loop does the `get events' half of the "get events,
  139.      *  process events" action of the infinite while.  this is to ensure
  140.      *  the event queue is always drained before the events that have come
  141.      *  in are processed.
  142.      */
  143.         do {
  144.  
  145.             XNextEvent(dpy, &event);
  146.                 switch (event.type) {
  147.  
  148.             /* "Expose" events are sort of like "REDRAW" in gl-speak in
  149.              *  terms of when a window becomes visible, or a previously
  150.              *  invisible part becomes visible.
  151.              */
  152.                 case Expose:                                   /* Exposures */
  153.                     myExpose = GL_TRUE;
  154.                     break;
  155.  
  156.  
  157.             /* "ConfigNotify" events are like "REDRAW" in terms of changes
  158.              *   to a window's size or position.
  159.              */
  160.                 case ConfigureNotify:                 /* Resize GL manually */
  161.             /* save the changed width/height of the parent X window */
  162.                     xsize = event.xconfigure.width;
  163.                     ysize = event.xconfigure.height;
  164.                     ratio  = (float) xsize / MAXRESOLUTION;
  165.                     myConfigure = GL_TRUE;
  166.                     break;
  167.  
  168.                 case ButtonRelease:                       /* Back door exit */
  169.                     if (event.xbutton.button == Button1)
  170.                         clean_exit();
  171.                     break;
  172.  
  173.                 case ClientMessage:                      /* WM invoked exit */
  174.                     if (event.xclient.data.l[0] == del_atom)
  175.                         clean_exit();
  176.                     break;
  177.  
  178.                 /* since interest is on the tablet, it becomes the default */
  179.                 default:   
  180.  
  181.                     if ((lineLs.tail != NULL) &&  /* make sure we've already
  182.                                                     processed a button (stylus)
  183.                                                     press event which sets up
  184.                                                     the linked-list for x/y 
  185.                                                     pair storage/line drawing */
  186.  
  187.                         (event.type == tablet_motion_type) &&  /* make sure
  188.                                                                  this is a mo-
  189.                                                                  tion event */
  190.  
  191.                          myButtDown) {  /* and make sure button itself is still
  192.                                            down--myButtPress only processes the
  193.                                            occurence of the but press (which 
  194.                                            sets up the new linked list) and 
  195.                                            then is immediately reset to FALSE */
  196.  
  197.                 /* the body of this if statement processes "Motion" events
  198.                  *  from any one of the dials.  the axes_count element of 
  199.                  *  the XDeviceMotionEvent structure (defined in /usr/include
  200.                  *  /X11/extensions/XInput.h) is used to determine if there 
  201.                  *  are 2 or only 1 new coordinate value(s):
  202.                  *  if (axes_count == 2), both X and Y have changed, 
  203.                  *  if (axes_count == 1), only Y has changed--X has not, and
  204.                  *  if (axes_count == 0), only X has changed--Y has not. 
  205.                  */
  206.                         XDeviceMotionEvent *M = (XDeviceMotionEvent *) &event;
  207.  
  208.                         if (M->axes_count != 2) {  /* if x OR y didn't chng */
  209.                             xprev = lineLs.tail->xy[X];/* axes_count < 2 so */
  210.                             yprev = lineLs.tail->xy[Y];/* need to save prev */
  211.                         }
  212.                         lineDummy       = newll();   /* alloc a new element */
  213.                         lineDummy->next = NULL;      /* for current line    */
  214.                         lineLs.tail->next = lineDummy;  /* point tail to it */
  215.                         lineLs.tail       = lineDummy;
  216.  
  217.                         if (M->axes_count == 2) {       /* if new x/y pair, */
  218.  
  219.                             lineLs.tail->xy[X] = M->axis_data[X];
  220.                             lineLs.tail->xy[Y] = M->axis_data[Y];
  221.  
  222.                         } else if (M->first_axis == 0) {/* elseif new X val */
  223.  
  224.                             lineLs.tail->xy[X] = M->axis_data[0];
  225.                             lineLs.tail->xy[Y] = yprev;
  226.  
  227.                         } else if (M->first_axis == 1) {/* elseif new Y val */
  228.  
  229.                             lineLs.tail->xy[X] = xprev;
  230.                             lineLs.tail->xy[Y] = M->axis_data[0];
  231.  
  232.                         }
  233.                         myMotion = GL_TRUE;
  234.                         
  235.                     } else if (event.type == tablet_press_type) {
  236.  
  237.                         XDeviceButtonEvent *P = (XDeviceButtonEvent *) &event;
  238.  
  239.                         xprev = P->axis_data[0]; /* butt's been pressed so  */
  240.                         yprev = P->axis_data[1]; /* now start to make a new */
  241.                         myButtPress = GL_TRUE;   /* line.  this cur pnt'll  */
  242.                         myButtDown = GL_TRUE;    /* be the "move to" coord  */
  243.  
  244.                     } else if (event.type == tablet_release_type) {
  245.  
  246.                         myButtRelease = GL_TRUE;
  247.                         myButtDown = GL_FALSE;
  248.                     }
  249.                     break;
  250.  
  251.             }  /* end switch (event.type) */
  252.  
  253.         } while (XPending(dpy));   /* end "do { } while".
  254.                                     * XPending() is like qtest()--it only
  255.                                     * tells you if there're any events
  256.                                     * presently in the queue.  it does not
  257.                                     * disturb queue's contents in any way.
  258.                                     */
  259.         
  260.     /* On an "Expose" event, redraw the affected window
  261.      */
  262.         if (myExpose) {
  263.             makeframe();                               /* draw the GL stuff */
  264.             myExpose = GL_FALSE;             /* reset flag--queue now empty */
  265.         }
  266.  
  267.     /* On a "ConfigureNotify" event, resize window (XMoveResizeWindow),
  268.      *  and then redraw contents.
  269.      */
  270.         if (myConfigure) {
  271.             glViewport(0, 0, xsize-1, ysize-1);
  272.             makeframe();
  273.             myConfigure = GL_FALSE;          /* reset flag--queue now empty */
  274.         }
  275.  
  276.     /* a motion-type event (the next x/y pair was already saved up above) 
  277.      *  means we're still drawing more along the current line.
  278.      */
  279.         if (myMotion) {
  280.             drawcurrentline();     /* butt still pressed, pen still moving, */
  281.                                    /* keep drawing at end of current line   */
  282.             myMotion = GL_FALSE;             /* reset flag--queue now empty */
  283.         }
  284.  
  285.     /* a "button press"-type event means we're starting a new line so we
  286.      *  need to re-initialize/create our linked-list.
  287.      */
  288.         if (myButtPress) {
  289.             XDeviceButtonEvent *B = (XDeviceButtonEvent *) &event;
  290.  
  291.             lineDummy       = newll();    /* making a new line so start a   */
  292.             lineDummy->next = NULL;       /* new list.  point head and tail */
  293.             lineLs.head = lineDummy;      /* to it, and assign current new  */
  294.             lineLs.tail = lineDummy;      /* point to "tail" of list        */
  295.             lineLs.tail->xy[X] = xprev;   /* make our first point be what   */
  296.             lineLs.tail->xy[Y] = yprev;   /* we saved up above              */
  297.             myButtPress = GL_FALSE;          /* reset flag--queue now empty */
  298.         }
  299.  
  300.     /* a "button release"-type event means the current line is complete
  301.      *  so now we need to free up the current linked-list.
  302.      */
  303.         if (myButtRelease) {
  304.             struct lineElement *ptr;
  305.             for (ptr = lineLs.head; ptr->next != NULL; ptr = ptr->next) {
  306.                 free(ptr);                   /* "empty" our current list    */
  307.             }
  308.             lineLs.head = NULL;
  309.             lineLs.tail = NULL;
  310.             myButtRelease = GL_FALSE;        /* reset flag--queue now empty */
  311.         }
  312.     }
  313. }
  314.  
  315.  
  316. static int attributeList[] = { None };   /* use the default graphics visual */
  317.  
  318.  
  319. /* WaitForNotify:
  320.  *   used to make sure the MapWindow() calls inside openwindow() occur
  321.  *   beFORE glXMakeCurrent() is invoked so as to avoid a race condition.
  322.  */
  323. static Bool WaitForNotify(Display *d, XEvent *e, char *arg) {
  324.     return (e->type == MapNotify) && (e->xmap.window == (Window)arg);
  325. }
  326.  
  327.  
  328. /*  openwindow -
  329.  *     establish connection to X server, get screen info, specify the
  330.  *     attributes we want the WM to try to provide, and create the GL window
  331.  */
  332. static void openwindow(char *progname) {
  333.  
  334.     int scrnnum;                             /* X screen number            */
  335.     int xorig, yorig;                        /* window (upper-left) origin */
  336.     long scrnheight;
  337.     XSizeHints Winhints;                        /* used to fix window size */
  338.     Colormap cmap;
  339.     GLXContext cx;
  340.     XVisualInfo *vi;
  341.     XSetWindowAttributes swa;
  342.     XColor colorstruct;
  343.  
  344.  
  345.    /* Connect to the X server and get screen info */
  346.     if ((dpy = XOpenDisplay(NULL)) == NULL) {
  347.         fprintf(stderr, "%s: cannot connect to X server %s\n",
  348.                                  progname, XDisplayName(NULL));
  349.         exit(1);
  350.     }
  351.     scrnnum = DefaultScreen(dpy);
  352.     scrnheight = DisplayHeight(dpy, scrnnum);
  353.  
  354.    /* define window (upper-left) origin coords */
  355.     xorig = 0;
  356.     yorig = 0;
  357.     xsize = 500;
  358.     ysize = 500;
  359.  
  360.     ratio = (float) xsize / MAXRESOLUTION;   /* calculate ratio to scale 
  361.                                                 full tablet into window */
  362.  
  363.     /* get an appropriate visual */
  364.     vi = glXChooseVisual(dpy, DefaultScreen(dpy), attributeList);
  365.     if (vi == NULL) {
  366.         printf("Couldn't get default visual (???)\n");
  367.         exit(0);
  368.     }
  369.  
  370.     /* create a GLX context */
  371.     cx = glXCreateContext(dpy, vi, NULL, GL_TRUE);
  372.     if (cx == NULL) {
  373.         printf("Couldn't get context.\n");
  374.         exit(0);
  375.     }
  376.  
  377.     /* create a colormap */
  378.     cmap = XCreateColormap(dpy, RootWindow(dpy, vi->screen),
  379.                            vi->visual, AllocAll);
  380.     /* create a window */
  381.     swa.colormap = cmap;
  382.     swa.border_pixel = 0;
  383.  
  384.     /* express interest in certain events */
  385.     swa.event_mask = StructureNotifyMask | ButtonPressMask |
  386.                        ButtonReleaseMask | ExposureMask;
  387.     glwin = XCreateWindow(dpy, RootWindow(dpy, vi->screen),
  388.                           xorig, yorig, xsize, ysize,
  389.                           0, vi->depth, InputOutput, vi->visual,
  390.                           CWBorderPixel|CWColormap|CWEventMask, &swa);
  391.  
  392.     XMapWindow(dpy, glwin);
  393.     XIfEvent(dpy, &event, WaitForNotify, (char*)glwin);
  394.  
  395.     XSync(dpy, GL_FALSE);
  396.     /* connect the context to the window */
  397.     if (!glXMakeCurrent(dpy, glwin, cx) == GL_TRUE) {
  398.         fprintf(stderr, "error w/glXMakeCurrent:  cudn't set");
  399.         fprintf(stderr, " context to the singlebuffered GL window\n");
  400.         exit(-1);
  401.     }
  402.  
  403.    /* specify the values for the Window Size Hints we want to enforce:  this
  404.     *  window's aspect ratio needs to stay at 1:1, constrain min and max
  405.     *  window size, and specify the initial origin and size of the window.
  406.     */
  407.     Winhints.x = xorig;         /* specify desired upper-left corner origin */
  408.     Winhints.y = yorig;         /* of window so prog will place itself      */
  409.     Winhints.width  = xsize;          /* specify desired x/y size of window */
  410.     Winhints.height = ysize;
  411.     Winhints.min_width = xsize/4;                     /* define min and max */
  412.     Winhints.max_width = scrnheight-1;                /* width and height   */
  413.     Winhints.min_height = ysize/4;
  414.     Winhints.max_height = scrnheight-1;
  415.     Winhints.min_aspect.x = 1;                /* keep aspect at a 1:1 ratio */
  416.     Winhints.max_aspect.x = 1;
  417.     Winhints.min_aspect.y = 1;
  418.     Winhints.max_aspect.y = 1;
  419.                                              /* set the corresponding flags */
  420.     Winhints.flags = USPosition|USSize|PMaxSize|PMinSize|PAspect;
  421.     XSetNormalHints(dpy, glwin, &Winhints);
  422.  
  423.    /* define string that will show up in the window title bar (and icon) */
  424.     XStoreName(dpy, glwin, "opengl tablet \"line drawing\" program");
  425.  
  426.    /* declare  interest in events we want the window to process */
  427.     XSelectInput(dpy, glwin, StructureNotifyMask | ExposureMask |
  428.                               ButtonPressMask | ButtonReleaseMask);
  429.  
  430.     XSetWMColormapWindows(dpy, glwin, &glwin, 1);
  431.  
  432.    /* express interest in WM killing this app */
  433.     if ((del_atom = XInternAtom(dpy, "WM_DELETE_WINDOW", GL_TRUE)) != None)
  434.         XSetWMProtocols(dpy, glwin, &del_atom, 1);
  435.  
  436.     colorstruct.pixel = YELLOW;
  437.     colorstruct.red   = 65535;
  438.     colorstruct.green = 65535;
  439.     colorstruct.blue  = 0;
  440.     colorstruct.flags = DoRed | DoGreen | DoBlue;
  441.     XStoreColor(dpy, cmap, &colorstruct);
  442.     colorstruct.pixel = BLUE;
  443.     colorstruct.red   = 0;
  444.     colorstruct.green = 0;
  445.     colorstruct.blue  = 35535;
  446.     colorstruct.flags = DoRed | DoGreen | DoBlue;
  447.     XStoreColor(dpy, cmap, &colorstruct);
  448.     colorstruct.pixel = CYAN;
  449.     colorstruct.red   = 0;
  450.     colorstruct.green = 65535;
  451.     colorstruct.blue  = 65535;
  452.     colorstruct.flags = DoRed | DoGreen | DoBlue;
  453.     XStoreColor(dpy, cmap, &colorstruct);
  454.  
  455.     glLineWidth(3.0);
  456.     glFlush();
  457. }
  458.  
  459.  
  460. /*  setupdevs - 
  461.  *
  462.  *   establish a live connection to the tablet device.
  463.  *
  464.  *   leverages off the "X11 Input Extension Library Specification" 
  465.  *   document (you *shud* be able to locate the on-line public access
  466.  *   directory which contains all the files to print hard-copy of this
  467.  *   document under .../mit/doc/extensions/xinput).  refer to
  468.  *   /usr/include/X11/extensions/{XI.h, XInput.h} for structures accessed.
  469.  */
  470. static void setupdevs() {
  471.  
  472.     int i, ndevices;
  473.     XDevice *tablet_device;
  474.     XDeviceInfoPtr lp, list;
  475.     int num_ext_event_classes;
  476.     XEventClass ListOfEventClass[3];
  477.     int tablet_press_class, tablet_release_class, tablet_motion_class;
  478.  
  479.  
  480.  
  481.     /* get a ptr to the list of all currently defined input devices */
  482.     list = (XDeviceInfoPtr) XListInputDevices(dpy, &ndevices);
  483.     if (!list) {
  484.         fprintf(stderr,"XlistInputDevices failed to generate a devices list\n");        exit(1);
  485.     }
  486.  
  487.    /* check out the /usr/people/4Dgifts/examples/devices/input/X/Xlist.c
  488.     *  program (which gets compiled into "xlist").  running it will list
  489.     *  all the currently available input devices on the machine xlist is
  490.     *  run on.  there is a LOT that should be studied in the "input" subtree.
  491.     */
  492.     for (lp=list, i=0; i<ndevices; lp++, i++) {
  493.         if (lp->use == IsXExtensionDevice && strcmp(lp->name,"tablet") == 0) {
  494.                 break;  /* found the right one--now save the ptr (lp) to it */
  495.         }
  496.     }
  497.     if (i == ndevices) {
  498.         fprintf(stderr, "\"tablet\" device not found\n");
  499.         exit(1);
  500.     }
  501.     tablet_device = XOpenDevice(dpy, lp->id);    /* open the Tablet device */
  502.     if (!tablet_device) {
  503.         fprintf(stderr, "XOpenDevice failedfor \"tablet\" device\n");
  504.         exit(1);
  505.     }
  506.     tablet_device_id = tablet_device->device_id;
  507.  
  508.    /* the following 3 macros determine the given event's type and class.
  509.     * each macro is passed the structure that describes the device from
  510.     * which input is desired.
  511.     */
  512.     DeviceButtonPress(tablet_device, tablet_press_type, tablet_press_class);
  513.     DeviceButtonRelease(tablet_device, tablet_release_type, tablet_release_class);
  514.     DeviceMotionNotify(tablet_device, tablet_motion_type, tablet_motion_class);
  515.  
  516.     ListOfEventClass[0]=tablet_press_class;
  517.     ListOfEventClass[1]=tablet_release_class;
  518.     ListOfEventClass[2]=tablet_motion_class;
  519.     num_ext_event_classes = 3;
  520.  
  521.    /* XSelectExtensionEvent requests the server to send events that match
  522.     * the events and devices described by the event list and that come
  523.     * from the requested window.
  524.     */
  525.     XSelectExtensionEvent(dpy, glwin, ListOfEventClass, num_ext_event_classes);
  526.  
  527. }
  528.  
  529.  
  530.  
  531. /*  draw the tablet's current line segment now that the event queue is drained
  532.  */
  533. static void drawcurrentline(void)
  534. {
  535.     struct lineElement *ptr;
  536. #ifdef GIVES_BROKEN_LINE__NOT_SURE_WHY
  537.     long vect[2];
  538.  
  539.     glIndexi(CYAN);
  540.  
  541.     glBegin(GL_LINES);                     /* draw our current line segment */
  542.  
  543.         for (ptr = lineLs.head; ptr->next != NULL; ptr = ptr->next) {
  544.             vect[0] = (long) (ptr->xy[0]*ratio);
  545.             vect[1] = (long) (ptr->xy[1]*ratio);
  546.             glVertex2i(vect[0], vect[1]);
  547.         }
  548.  
  549.     glEnd();
  550. #endif
  551.     int vect1[2];
  552.     int vect2[2];
  553.  
  554.  
  555.     glIndexi(CYAN);
  556.  
  557.     for (vect1[0] = (int) (lineLs.head->xy[0]*ratio),  /* initially get the */
  558.          vect1[1] = (int) (lineLs.head->xy[1]*ratio),  /* first vertex      */
  559.          ptr = lineLs.head->next; ptr->next != NULL; ptr = ptr->next) {
  560.  
  561.         vect2[0] = (int) (ptr->xy[0]*ratio);         /* get the next vertex */
  562.         vect2[1] = (int) (ptr->xy[1]*ratio);
  563.  
  564.         glBegin(GL_LINES);                 /* draw our current line segment */
  565.             glVertex2i(vect1[0], vect1[1]);
  566.             glVertex2i(vect2[0], vect2[1]);
  567.         glEnd();
  568.  
  569.         vect1[0] = vect2[0];          /* now save the last vertex into the  */
  570.         vect1[1] = vect2[1];          /* beginning of the next line segment */
  571.     }
  572. }
  573.  
  574.  
  575.  
  576. /*  makeframe -- Draw the tablet "background" in the GL window
  577.  */
  578. static void makeframe()
  579. {
  580.     glClearIndex((GLfloat) BLUE);
  581.     glClear(GL_COLOR_BUFFER_BIT);
  582.     glLoadIdentity();
  583.     gluOrtho2D(-0.5, xsize-0.5, -0.5, ysize-0.5);
  584.  
  585.     glIndexi(YELLOW);
  586.     glRasterPos2i(4, 5);
  587.     printString("Use left mouse button to quit.");
  588.  
  589.     glFlush();
  590. }
  591.  
  592.  
  593. /* makeRasterFont() and printString() are lifted out of font.c (lives in
  594.  * this same directory) as a replacement to the IrisGL charstr() function.
  595.  */
  596. GLuint base;
  597.  
  598. static void makeRasterFont(Display **dpy)
  599. {
  600.     XFontStruct *fontInfo;
  601.     Font id;
  602.     unsigned int first, last;
  603.  
  604.     fontInfo = XLoadQueryFont(*dpy,
  605.       "-sgi-screen-bold-r-normal--15-150-72-72-m-90-iso8859-1");
  606.     if (fontInfo == NULL) {
  607.         printf("no font found\n");
  608.         exit(0);
  609.     }
  610.     id = fontInfo->fid;
  611.     first = fontInfo->min_char_or_byte2;
  612.     last = fontInfo->max_char_or_byte2;
  613.     base = glGenLists(last+1);
  614.     if (base == 0) {
  615.         printf("out of display lists\n");
  616.         exit(0);
  617.     }
  618.     glXUseXFont(id, first, last-first+1, base+first);
  619. }
  620.  
  621. static void printString(char *s)
  622. {
  623.     glPushAttrib(GL_LIST_BIT);
  624.     glListBase(base);
  625.     glCallLists(strlen(s), GL_UNSIGNED_BYTE, (unsigned char *)s);
  626.     glPopAttrib();
  627. }
  628.  
  629.  
  630.  
  631. /*  clean_exit  --  Clean up before exiting 
  632.  */
  633. static void clean_exit(void)
  634. {
  635.     XCloseDisplay(dpy);
  636.     exit(0);
  637. }
  638.